﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.ComponentModel;

namespace Arch.Model
{
    [DataContract(IsReference = true)]
    public abstract class Entity : IEntity
    {
        public virtual bool IsTransient { get { throw new NotImplementedException(); } }
    }

    [DataContract(Name = "EntityOfType{0}", IsReference = true)]
    public abstract class Entity<TId> : Entity, IEntity<TId>
    {
        [DataMember]
        [Key, Display(AutoGenerateField = false)]
        public virtual TId Id { get; set; }

        public override bool IsTransient
        {
            get { return Id == null || Id.Equals(default(TId)); }
        }

        public override bool Equals(object obj)
        {
            Entity<TId> compareTo = obj as Entity<TId>;

            return (compareTo != null) && (HasSameNonDefaultIdAs(compareTo) ||
               (((IsTransient) || compareTo.IsTransient) && HasSameBusinessSignatureAs(compareTo)));
        }

        public override int GetHashCode()
        {
            if (_hashcodeCache.HasValue) return _hashcodeCache.Value;

            if (IsTransient) _hashcodeCache = base.GetHashCode();
            else unchecked
                {
                    _hashcodeCache = (GetType().GetHashCode() * HashcodeMultiplier) ^ Id.GetHashCode();
                }

            return _hashcodeCache.Value;
        }

        private bool HasSameBusinessSignatureAs(IEntity compareTo)
        {
            return GetHashCode().Equals(compareTo.GetHashCode());
        }

        private bool HasSameNonDefaultIdAs(IEntity<TId> compareTo)
        {
            return (Id != null && !Id.Equals(default(TId)))
                && (compareTo.Id != null && !compareTo.Id.Equals(default(TId)))
                && Id.Equals(compareTo.Id);
        }

        private const int HashcodeMultiplier = 33;
        private int? _hashcodeCache;
    }
}